package com.yycx.module.file.provider.oss.common;

import com.aliyun.oss.OSS;
import com.aliyun.oss.model.*;
import com.yycx.common.base.utils.FlymeUtils;
import com.yycx.module.file.provider.oss.client.AliOssClient;
import com.yycx.module.file.provider.oss.listener.PutObjectProgressListener;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.security.core.parameters.P;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author HY
 * @version V1.0
 * @ClassName: MultipartUpload
 * @Description: 文件分片上传处理类
 * @date 2018年4月25日 下午6:05:26
 */
@Slf4j
public class MultipartUpload {

    private OSS client = null;
    private String objectID;
    private String bucketName;
    /**
     * 创建线程池
     */
    ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build());
    private List<PartETag> partETags = Collections.synchronizedList(new ArrayList<PartETag>());

    public MultipartUpload(OSS client, String objectID, String bucketName) {
        this.client = client;
        this.objectID = objectID;
        this.bucketName = bucketName;
    }

    /**
     * @Title: sliceUploadToAliyun
     * @Description: 分片上传
     * @Return
     */
    public String sliceUploadToAliyun(InputStream inputStream, long fileLength) {
        String uploadId = "";
        try {

            uploadId = claimUploadId();
            //计算分片的数量
            long partSize = AliOssClient.MAX_PART_SIZE;
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }

            //分片的片数不能超过10000
            if (partCount > 10000) {
                throw new RuntimeException("上传片数不能超过10000");
            }

            //多线程上传
            for (int j = 0; j < partCount; j++) {
                long startPos = j * partSize;
                long curPartSize = (j + 1 == partCount) ? (fileLength - startPos) : partSize;
                executorService.execute(new PartUploader(inputStream, startPos, curPartSize, j + 1, uploadId));
            }

            //关闭线程池
            executorService.shutdown();

            //等待完成所有分片
            while (!executorService.isTerminated()) {
                try {
                    executorService.awaitTermination(5, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            //完成分片
            completeMultipartUpload(uploadId);

        } catch (Exception e) {
            e.printStackTrace();
            log.error("OSS分片上传失败，" + e.getMessage());
            //分片上传失败的同时，将其取消
            if (StringUtils.isNotBlank(uploadId)) {
                canceUpload(uploadId);
            }
        }
        return objectID;
    }

    /**
     * @Title: sliceUploadToAliyun
     * @Description: 分片上传
     * @Return
     */
    public String sliceUploadToAliyun(File file, long fileLength) {
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return sliceUploadToAliyun(inputStream, fileLength);
    }

    /**
     * @author HY
     * @version V1.0
     * @ClassName: PartUploader
     * @Description: 内部类，分片使用
     * @date 2018年4月25日 下午6:04:57
     */
    private class PartUploader implements Runnable {

        private File file;//文件对象
        private InputStream inputStream;//文件流
        private long startPos;//分片起始位置

        private long partSize;//片数
        private int partNumber;//分片号
        private String uploadId;//ID

        public PartUploader(File file, long startPos, long partSize, int partNumber, String uploadId) {
            this.file = file;
            this.startPos = startPos;
            this.partSize = partSize;
            this.partNumber = partNumber;
            this.uploadId = uploadId;
        }

        public PartUploader(InputStream inputStream, long startPos, long partSize, int partNumber, String uploadId) {
            this.inputStream = inputStream;
            this.startPos = startPos;
            this.partSize = partSize;
            this.partNumber = partNumber;
            this.uploadId = uploadId;
        }

        @Override
        public void run() {
            try {
                if (FlymeUtils.isNotEmpty(file)) {
                    inputStream = new FileInputStream(file);
                }
                inputStream.skip(this.startPos);//设置流的起始位置
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(bucketName);
                uploadPartRequest.setKey(objectID);
                uploadPartRequest.setUploadId(this.uploadId);//设置uploadId，一个文件的所有分片的uploadId都一样
                uploadPartRequest.setInputStream(inputStream);//文件流
                uploadPartRequest.setPartSize(this.partSize);//总片数
                uploadPartRequest.setPartNumber(this.partNumber);//分片号
                uploadPartRequest.setProgressListener(new PutObjectProgressListener());
                UploadPartResult uploadPartResult = client.uploadPart(uploadPartRequest);
                synchronized (partETags) {
                    partETags.add(uploadPartResult.getPartETag());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @return
     * @Title: claimUploadId
     * @Description: 获取uploadId，来自于initiateMultipartUpload
     * @Author HY
     * @Date 2018年4月25日 下午6:25:37
     * @Return
     * @Throws
     */
    private String claimUploadId() {
        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectID);
        InitiateMultipartUploadResult result = client.initiateMultipartUpload(request);
        return result.getUploadId();
    }

    /**
     * @param uploadId
     * @Title: canceUpload
     * @Description: 取消分片上传，同时已经上传的Part数据也会被删除
     * @Author HY
     * @Date 2018年4月25日 下午6:27:38
     * @Return
     */
    private void canceUpload(String uploadId) {
        try {
            // 取消分片上传，其中uploadId来自于initiateMultipartUpload
            AbortMultipartUploadRequest abortMultipartUploadRequest =
                    new AbortMultipartUploadRequest(bucketName, objectID, uploadId);
            client.abortMultipartUpload(abortMultipartUploadRequest);
        } catch (Exception e) {
            log.error("取消分片上传失败，" + e.getMessage());
        }
    }

    /**
     * @param uploadId
     * @Title: completeMultipartUpload
     * @Description: 完成分片
     * @Author HY
     * @Date 2018年4月25日 下午6:20:13
     * @Return
     */
    private void completeMultipartUpload(String uploadId) {
        Collections.sort(partETags, new Comparator<PartETag>() {
            @Override
            public int compare(PartETag p1, PartETag p2) {
                return p1.getPartNumber() - p2.getPartNumber();
            }
        });

        CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName,
                objectID, uploadId, partETags).withProgressListener(new PutObjectProgressListener());
        client.completeMultipartUpload(completeMultipartUploadRequest);
    }

}
